package com.atguigu.gmall.item.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.cache.aspect.annotation.MallCache;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.cache.service.CacheService;
import com.atguigu.gmall.item.service.ItemService;
import com.atguigu.gmall.product.eneity.SkuImage;
import com.atguigu.gmall.product.eneity.SkuInfo;
import com.atguigu.gmall.product.eneity.SpuSaleAttr;
import com.atguigu.gmall.product.vo.CategoryTreeVo;
import com.atguigu.gmall.product.vo.SkuDetailVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    CacheService cacheService;

    @Autowired
    RedissonClient redissonClient;


    @MallCache(cacheKey = RedisConst.SKU_DETAIL_CACHE+"#{#args[0]}",
                bitMapName = RedisConst.SKUID_BITMAP,
                bitMapKey = "#{#args[0]}",
                lockKey = RedisConst.SKU_LOCK+"#{#args[0]}",
                ttl = 7,
                unit = TimeUnit.DAYS)
    @Override
    public SkuDetailVo getItemDetail(Long skuId){
        return getDataFromRpc(skuId);
    }

    /**
     * 查询商品详情(分布式锁版)
     * @param skuId
     * @return
     */
    /*public SkuDetailVo getItemDetail3(Long skuId) throws InterruptedException {

        SkuDetailVo returnVal = null;
        //1.先查缓存
        log.info("商品详情查询开始");
        returnVal = cacheService.getFromCache(skuId);
        if (returnVal != null){
            return returnVal;
        }
        //2.如果缓存为空，则先查位图
        boolean mightContain = cacheService.mightContain(skuId);
        if (!mightContain){
            log.info("疑似攻击请求");
            return null;
        }
        //3.回源，查数据库
        log.info("bitmap中有，准备回源");
        //4.准备拦截缓存击穿风险
        RLock lock = redissonClient.getLock(RedisConst.SKU_LOCK + skuId);
        boolean trylock = false;
        try {
            trylock = lock.tryLock();
            if (trylock){
                //抢到锁，准备回源
                returnVal = getDataFromRpc(skuId);
                //将数据库查询到的数据放到redis中
                cacheService.saveData(skuId,returnVal);
                return returnVal;
            }else {
                    TimeUnit.MILLISECONDS.sleep(300l);
                    return cacheService.getFromCache(skuId);
            }
        }finally {
            if (trylock){
                try {
                    lock.unlock();
                }catch (Exception e){}
            }

        }
    }

    ReentrantLock lock = new ReentrantLock();
    *//**
     * 查询商品详情(本地锁版)
     * @param skuId
     * @return
     *//*
    public SkuDetailVo getItemDetail2(Long skuId) {
        SkuDetailVo returnVal = null;
        //1.先查缓存
        log.info("商品详情查询开始");
        returnVal = cacheService.getFromCache(skuId);
        if (returnVal == null){
            //位图判定是否有
            boolean contain = cacheService.mightContain(skuId);
            if (!contain){
                log.info("疑似攻击请求");
                return null;
            }
            //回源，查数据库
            log.info("bitmap有，准备回源");
            //准备拦截击穿风险
            boolean tryLock = lock.tryLock();
            if (tryLock){
                returnVal = getDataFromRpc(skuId);
                //4.保存到缓存
                cacheService.saveData(skuId,returnVal);
                lock.unlock();
            }else {
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                    returnVal = cacheService.getFromCache(skuId);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return returnVal;
    }*/

    /**
     * 查询商品详情1
     * @param skuId
     * @return
     */
    public SkuDetailVo getItemDetail1(Long skuId) {
        //1.先查缓存，skuinfo:id = 商品json
        String json = stringRedisTemplate.opsForValue().get("sku:info:" + skuId);
        //如果为空，就回源
        if (StringUtils.isEmpty(json)){
            SkuDetailVo skuDetailVo = getDataFromRpc(skuId);
            String jsonData = "x";
            //将数据库查询的数据放到redis中

            if (skuDetailVo != null){
                jsonData = JSON.toJSONString(skuDetailVo);
            }
                stringRedisTemplate.opsForValue().set("sku:info"+skuId, jsonData,7, TimeUnit.DAYS);
                return skuDetailVo;
        }
        //如果数据库查询的数据真有：json真数据   假有：x
        if ("x".equals(json)){
            log.info("疑似攻击请求");
            return null;
        }
        SkuDetailVo skuDetailVo = JSON.parseObject(json, SkuDetailVo.class);
        return skuDetailVo;
    }

    private SkuDetailVo getDataFromRpc(Long skuId) {
        SkuDetailVo skuDetailVo = new SkuDetailVo();
        //1.当前商品精确完整信息、图片
        CompletableFuture<SkuInfo> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfo skuInfo = skuDetailFeignClient.getSkuInfo(skuId).getData();
            return skuInfo;
        },threadPoolExecutor);

        CompletableFuture<Void> ImageListFuture = skuInfoFuture.thenAcceptAsync((data) -> {
            if (data ==null){return;}
            List<SkuImage> skuImages = skuDetailFeignClient.getSkuImages(skuId).getData();
            data.setSkuImageList(skuImages);
            skuDetailVo.setSkuInfo(data);
        },threadPoolExecutor);

        //2.获取商品详情
        CompletableFuture<Void> skuFuture = skuInfoFuture.thenAcceptAsync((data) -> {
            if (data ==null){return;}
            CategoryTreeVo categoryTreeVo = skuDetailFeignClient.getCategoryTreeWithC3Id(data.getCategory3Id()).getData();
            SkuDetailVo.categoryView categoryView = new SkuDetailVo.categoryView();
            //一级分类id和name
            categoryView.setCategory1Id(categoryTreeVo.getCategoryId());
            categoryView.setCategory1Name(categoryTreeVo.getCategoryName());
            //二级分类id和name
            categoryView.setCategory2Id(categoryTreeVo.getCategoryChild().get(0).getCategoryId());
            categoryView.setCategory2Name(categoryTreeVo.getCategoryChild().get(0).getCategoryName());
            //三级分类id和name
            CategoryTreeVo vo = categoryTreeVo.getCategoryChild().get(0);
            categoryView.setCategory3Id(vo.getCategoryChild().get(0).getCategoryId());
            categoryView.setCategory3Name(vo.getCategoryChild().get(0).getCategoryName());
            skuDetailVo.setCategoryView(categoryView);
        },threadPoolExecutor);

        //3.商品实时价格
        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            try {
                BigDecimal price = skuDetailFeignClient.getPrice(skuId).getData();
                skuDetailVo.setPrice(price);
            }catch (Exception e){

            }
        },threadPoolExecutor);

        //4.销售属性
        CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((data) -> {
            if (data ==null){return;}
            List<SpuSaleAttr> spuSaleAttrList = skuDetailFeignClient.getSpuSaleAttr(data.getSpuId(), skuId).getData();
            skuDetailVo.setSpuSaleAttrList(spuSaleAttrList);
        },threadPoolExecutor);

        //5.ValuesJson
        CompletableFuture<Void> valuesJsonFuture = skuInfoFuture.thenAcceptAsync((item) -> {
            if (item ==null){return;}
            String data = skuDetailFeignClient.getValuesJson(item.getSpuId()).getData();
            skuDetailVo.setValuesJson(data);
        },threadPoolExecutor);
        CompletableFuture.allOf(valuesJsonFuture,saleAttrFuture,priceFuture,skuFuture,ImageListFuture)
                            .join();
        return skuDetailVo;
    }
}
